home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Information / Programming / Writing System Extensions / Writing System Extensions 2⁄3 < prev    next >
Encoding:
Text File  |  1994-12-03  |  27.5 KB  |  659 lines  |  [TEXT/R*ch]

  1. [8] How do I write a Control Panel-INIT combination?
  2.  
  3. System extensions generally have no interface.  They do what they do 
  4. quietly and the user doesn't want to hear from them.  In many cases 
  5. the user needs to set certain preferences.  The natural mechanism for 
  6. this is to couple a control panel with an INIT.  The problem of 
  7. course is how does the control panel communicate the changes to the 
  8. INIT.  I won't discuss the general mechanisms of control panel 
  9. authoring here (see NIM: More Macintosh Toolbox, Chapter 8), but 
  10. there are several mechanisms available for communicating between 
  11. extensions and control panels.
  12.  
  13. The simplest mechanism and one that will work in the vast majority of 
  14. cases is for the extension to install a Gestalt selector.  This 
  15. selector returns the address of a block of memory that holds 
  16. variables that control the actions of the extension.  The control 
  17. panel calls the Gestalt selector, retrieves the address of the memory 
  18. block, and alters the values stored in the block as needed.  In some 
  19. cases the memory block can contain a function pointer to a function 
  20. in the extension that needs to be called from the control panel.  
  21. Here is some sample code:
  22.  
  23. typedef struct CommonInfo{
  24.    void     (*ResetINIT) (void);//function pointer to reset 
  25.                     //func
  26.    Boolean  On;
  27. };
  28.  
  29. #define kSignature 'BLAH'  //Should be the sig of the INIT
  30.  
  31. static CommonInfo    gInfo;
  32.  
  33. /**InstallGestaltSelector****************************************/
  34. //In the INIT; runs at INIT time
  35. OSErr
  36. InstallGestaltSelector(void)
  37. {
  38.    OSErr    err;
  39.  
  40.    err = NewGestalt( kSignature, OurSelector );
  41.    
  42.    if ( err == noErr )
  43.    {
  44.       gInfo.ResetINIT = (void *) ResetFunction;
  45.       gInfo.On = TRUE;
  46.    }
  47.  
  48.    return err;
  49. }
  50.  
  51. /**OurSelector**************************************************/
  52. //In the INIT
  53. pascal OSErr 
  54. OurSelector( OSType theSelector, long *theResponse )
  55. {
  56.    SetUpA4();
  57.  
  58.    *theResponse = (long) &gInfo;
  59.    
  60.    RestoreA4();
  61.  
  62.    return noErr;
  63. }
  64.  
  65. /**ResetFunction**************************************************/
  66. //In the INIT
  67. void 
  68. ResetFunction(void)
  69. {
  70.    SetUpA4();
  71.  
  72.    //Do something here
  73.    
  74.    RestoreA4();
  75. }
  76.  
  77. /**Close********************************************************/
  78. //In the Control Panel
  79. void
  80. Close( Boolean IsOn )
  81. {
  82.    long        result;
  83.    CommonInfo     *Info;
  84.    
  85.    //Get address of globals struct from init
  86.    err = Gestalt( kSignature, &result );
  87.    if ( err == noErr )
  88.    {
  89.       Info = (GlobalsType *) result;
  90.       Info->On = IsOn;        //Reset OnOff Boolean
  91.       ( * (*Info).ResetINIT) (); //Jump to INIT
  92.    }
  93. }
  94.  
  95. If it is possible for an error to occur that prevents the extension 
  96. from resetting itself the ResetFunction should return an error code 
  97. and the control panel should display an alert indicating the problem.
  98.  
  99. Two additional methods are sometimes used to accomplish communication 
  100. between an extension and a control panel:  
  101.  
  102. The first of these is to write a driver that is installed by the 
  103. extension.  The driver holds global variables and will return the 
  104. address of the block of memory holding these variables in response to 
  105. i/o, status, or control calls to the driver.  Sample code showing how 
  106. to do this is available in a package called driver-22 written by Pete 
  107. Resnick to be found at:
  108. ftp://sumex-aim.stanford.edu/info-mac/dev/src/driver-22-c.hqx.gz
  109.  
  110. The second additional method is to use the PPC toolbox for direct 
  111. communication.  There is sample code demonstrating this at: 
  112. ftp://ftp.apple.com/dts/mac/sc/7.0.samples/init-cdev.hqx.
  113.  
  114. --
  115. [9] How do I capture keystrokes?
  116.  
  117. This is accomplished by writing a jGNEFilter function.  jGNEFilter 
  118. functions are called from GetNextEvent and WaitNextEvent just before 
  119. those traps return to an application.  They are passed a pointer to 
  120. the event record that will be returned to the application.  In order 
  121. to capture keystrokes the jGNEFilter would simply check the what 
  122. field of the event record looking for keyboard events and would 
  123. extract the information from the message field if one were found.  
  124.  
  125. The jGNEFilter mechanism is very powerful and is one way that 
  126. screensavers can be implemented.  The filter would save the time of 
  127. any keyboard events and would compare the location of the mouse 
  128. against its previous location on null events.  If the preset time had 
  129. elapsed during which no keyboard events or mouse movement had 
  130. occurred then the screen saver would activate.  A more complete 
  131. discussion of the jGNEFilter is in the next section.
  132.  
  133. If you are thinking of patching WaitNextEvent or PostEvent in order 
  134. to capture keystrokes or other events, don't.  Use a jGNEFilter 
  135. instead.  It's easier, it's compatible.  It's even documented.  See 
  136. the Tech Note 'GetNextEvent; Blinking Apple Menu' (#85).
  137.  
  138. ftp://ftp.apple.com/dts/mac/tn/toolbox.tb/tb-11-getnextevent.hqx
  139.  
  140. --
  141. [10] How can my extension get time periodically?
  142.  
  143. Installing a jGNEFilter is one method of obtaining periodic time.  
  144. Since the jGNEFilter mechanism is dependent on the event processing 
  145. mechanism, one problem is that no events may be posted if the mouse 
  146. is held down for an extended time.  
  147.  
  148. If your INIT only needs to get time every once in a while then I 
  149. recommend that it only do its thing on null events.  On other events 
  150. it should just return.  This will have the least impact on the 
  151. machine's performance.  
  152.  
  153. Remember that your jGNEFilter will be called for EVERY event on the 
  154. machine.  Do not do a lot of processing on every event or you will be 
  155. slowing down the machine needlessly.  Another way to reduce the 
  156. frequency of your extension's processing is to use a simple timer, 
  157. based on TickCount().  In this way your processing is only done, say, 
  158. every 30 or 60 ticks, on null events of course.
  159.  
  160. Sample code demonstrating jGNEFilters can be found in a package 
  161. called jGNE Helper by Pete Gontier in the alt.sources.mac archive at: 
  162. ftp://ftpbio.bgsu.edu/ftp/pub/alt.sources.mac/vol-
  163. 01/jgnehelper.cpt.hqx.  
  164.  
  165. Another sample in MPW assembler is at 
  166. ftp://ftp.apple.com/dts/mac/sc/snippets/toolbox/jgnefilter.hqx.
  167.  
  168. Here's another example that works in Think C:
  169.  
  170. static   ProcPtr  gOldGNEFilter;
  171.  
  172. /****InstallFilter***********************************************/
  173. //Run this at INIT time
  174. void
  175. InstallGNEFilter (void)
  176. {
  177. /*Save the ProcPtr to the previous jGNEFilter and insert ours*/
  178.    gOldGNEFilter = JGNEFilter;
  179.    JGNEFilter = (ProcPtr) StripAddress( FilterProc );
  180.  
  181.  }
  182.  
  183. /****FilterProc**************************************************/
  184.  
  185. void
  186. FilterProc(void)
  187. {
  188.    EventRecord    *theEvent;
  189.    long        SaveD0;
  190.    
  191.    theEvent = GetA1();  //Move the eventPtr to a variable
  192.    SaveD0 = GetD0();    //Preserve D0
  193.  
  194.    SetUpA4();
  195.  
  196.    switch ( (*theEvent).what ) {
  197.       case nullEvent:
  198.          //Do our thing
  199.          break;
  200.          
  201.       case keyDown:
  202.          //Do something else
  203.          break;
  204.    }
  205.    
  206.    //Execute the previous jGNEFilter
  207.    SetA1( theEvent );   //Restore A1 for the next jGNEFiler
  208.    SetD0( SaveD0);      //Restore D0
  209.    SetA0( gOldGNEFilter ); //Put next jGNEFilter in A0
  210.    
  211.    RestoreA4();
  212.  
  213.    asm{
  214.       Unlk     A6
  215.       Move.W   D0, 4(A7)   ;Set Function result on the stack
  216.       JMP      (A0)     ;Jump to the next jGNEFilter
  217.    }
  218.  
  219. }
  220.  
  221. Since a jGNEFilter has access to the actual event record that will be 
  222. returned to the application, the filter can alter the event.  The 
  223. most common thing to do is to 'cancel' an event by changing it to a 
  224. null event (Ex. theEvent->what = nullEvent).  In this case it should 
  225. also set the value in register D0 to False, or zero.
  226.  
  227. Here are some additional methods for an extension to get time 
  228. periodically:
  229.  
  230. If your extension needs more frequent or regular time then can be 
  231. provided by a jGNEFilter then you can install a VBL task or a time 
  232. manager task.  Since these run at interrupt time they cannot do 
  233. anything that could move or purge memory.  Other methods include 
  234. patching a trap that is called frequently, like SetPort.
  235.  
  236. A faceless Notification Manager request is another method to get 
  237. time.  This is a request that has all the fields in the notification 
  238. record set to NULL except the nmResp field that holds the address of 
  239. your routine to be executed.  Your notification response routine will 
  240. be called soon and will be able to move memory.  If necessary the 
  241. routine can reinstall itself.  This technique is useful for tasks 
  242. that need to be executed once or intermittently.  For those tasks 
  243. that need to be executed regularly use one of the other techniques.
  244.  
  245. You could of course have a faceless NM request that is installed by a 
  246. VBL task or a time manager task.  
  247.  
  248. Use of a faceless background application in concert with an extension 
  249. is yet another method to get time.  The fba would do its work on its 
  250. null events.
  251.  
  252. --
  253. [11] How should an INIT manage memory?
  254.  
  255. In general, at INIT time extensions will want to allocate memory in 
  256. the system heap.  If you are allocating pointers or handles, use the 
  257. SYS variants, like NewHandleSys() and NewPtrSys().  It is a common 
  258. error to forget this and then to wonder why the INIT crashes.  If you 
  259. call NewHandle at INIT time, the handle will be allocated in the 
  260. temporary heap allocated for your extension.  If your INIT attempts 
  261. to use it after INIT time the temporary heap will be long gone, along 
  262. with any handles or pointers that had been allocated in it.  Any 
  263. attempt to use handles or pointers that no longer exist are 
  264. predictably unpredictable :-) 
  265.  
  266. NewHandle and NewPtr will allocate their memory blocks in the system 
  267. heap if the zone has been set to the system heap, as shown in the 
  268. next paragraph.
  269.  
  270. If you need to read in resources you can ensure that they go into the 
  271. system heap with something like the following code:
  272.  
  273. THz   saveZone = GetZone();
  274.    SetZone( SystemZone() );
  275.    //read in resources
  276.    SetZone( saveZone );
  277.  
  278. You will of course need to detach the resources if they need to 
  279. remain in memory after your extension exits.  The resource file 
  280. containing your extension will be closed when your extension exits.
  281.  
  282. If you need to read resources into memory after INIT time you need to 
  283. decide which heap they should go into, either the application heap or 
  284. the system heap.  It's a bit hard to make a specific recommendation 
  285. on this but if the resource is something that the application is 
  286. expecting to be read in then it should go into the application heap.  
  287. This would include things like WIND resources in a trap patch to 
  288. GetNewWindow().  The problem of course is that the application heap 
  289. may not have enough room.  However, if the resources are private to 
  290. the extension then they should go into the system heap.
  291.  
  292. It should go without saying that you should always check the error 
  293. codes on memory allocating calls and resource manager calls.  If your 
  294. extension is doing something in response to a user action, say making 
  295. a network connection, then it is appropriate to report such errors.  
  296. In many cases however, your extension will simply do nothing in the 
  297. case of out of memory errors or missing resource errors.  It is best 
  298. to attempt to allocate all of these things at INIT time and if 
  299. unsuccessful to bail out then.
  300.  
  301. As mentioned above, MoveHHi doesn't work in the system heap.  If you 
  302. intend to allocate a handle that will remain locked for extended 
  303. periods, then call ResrvMem before allocating and locking the handle.  
  304. This will place the handle low in the system heap and help to prevent 
  305. heap fragmentation. 
  306.  
  307. Many toolbox calls are documented as not moving or purging memory and 
  308. as being safe to call at interrupt time.  If you are patching one of 
  309. these traps then you must preserve this property.  You are guaranteed 
  310. to cause other software to crash if you don't, and your users will 
  311. hate you (once they figure out that it's you).  Be aware that the 
  312. only memory manager routine safe to call under these circumstances is 
  313. BlockMove. Also be aware that it is unsafe to access an unlocked 
  314. handle at interrupt time.  It is possible that the memory manager is 
  315. in the midst of moving it from one place to another in the heap, and 
  316. the master pointer may not be updated yet.
  317.  
  318. --
  319. [12] How do I get my INIT to turn itself off?
  320.  
  321. An extension might want to turn itself off at INIT time based on its 
  322. preferences setting, or based on a key being pressed or the mouse 
  323. button being pressed, or due to an error during initialization.  
  324. Extensions usually show an icon with a red X through it in this case.  
  325. An extension might also want to turn itself off temporarily after 
  326. INIT time in response to its Control Panel.  For instance GateKeeper 
  327. has an on/off switch that turns off virus checking for a set time.
  328.  
  329. The strategy for temporarily turning off an extension is simply to 
  330. set a global flag and to check it from within the trap patches or 
  331. other parts of the extension.  If the flag is off then the trap patch 
  332. simply executes the previous trap.  It is generally unsafe to unpatch 
  333. or patch traps after INIT time from an extension.  The reason for 
  334. this is that if another extension patches the same trap after you 
  335. have, then it will be jumping to your patch when it has completed its 
  336. work.  If you have removed your patch then this calling chain will be 
  337. disrupted and bad things will happen.  Also, the Finder patches 
  338. various traps when it loads, which is after INIT time.  Disrupting 
  339. those patches would be a very bad thing.
  340.  
  341. If an extension determines at INIT time that it isn't going to stay 
  342. around then it shouldn't call DetachResource on itself.  It's best 
  343. that your extension determine that anything it's dependent on, such 
  344. as resources, specific system Managers, and sufficient memory, are 
  345. present *before* it starts to patch traps and install drivers, 
  346. jGNEFilters and so on.  It would be a very bad idea for an extension 
  347. to not detach itself after it had already patched a trap if the trap 
  348. patch resided in the extension.
  349.  
  350. Many extensions use a particular key press as a signal to indicate 
  351. that the user wants them not to run.  Of course the system uses the 
  352. shift key as a signal not to turn on any extensions so you can't use 
  353. that.  Some extensions use the option or command keys for this 
  354. purpose.  The problem with using those keys is that every time I 
  355. rebuild the desktop those extensions are needlessly inactivated.  I 
  356. recommend that the space bar be used for this purpose.  Here's some 
  357. sample code:
  358.  
  359.  Boolean
  360.  SpaceBarIsDown(void)
  361.  {
  362.    KeyMap      theKeys;
  363.  
  364.    GetKeys( theKeys );
  365.  
  366.    if ( theKeys[1] & 0x00000200 )//Check for spacebar
  367.       return TRUE;
  368.    else
  369.       return FALSE;
  370.  
  371.  }
  372.  
  373. --
  374. [13] How do I get my INIT to show it's icon like all the other cool 
  375. inits do?
  376.  
  377. This is easy.  There is code available that does this for you.  Get a 
  378. package written by Jim Walker called ShowIcon7 at:
  379. ftp://mac.archive.umich.edu/mac/development/source/showicon7.sit.hqx
  380.  
  381. You use it essentially as a plug-in.  Just pass in the icon's 
  382. resource ID and it does everything for you.  It also shows how to set 
  383. up an A5 world in an extension.  You might also take a look at Dair 
  384. Grant's Extension shell package.  This one shows how to do animated 
  385. icons.
  386.  
  387. If your extension decides that it can't install itself then it passes 
  388. the resource ID of an icon that has a red X through it to the 
  389. showicon7 code resource.
  390.  
  391. Some extensions include the ShowIcon code within their own code 
  392. resources.  This code is only about 1K but it seems pointless to me 
  393. for this code to sit in the system heap when it doesn't have to be.  
  394. Use it as a plug-in for your extensions.
  395.  
  396. --
  397. [14] How do I show a dialog from my INIT?
  398.  
  399. First of all, I hate windows of any kind during the startup process.  
  400. If all you want to do is show an alert then use the Notification 
  401. Manager.  Your alert will show up when the Finder starts but the user 
  402. will see it and will get whatever message you need to send.  
  403.  
  404. The problem I have with windows at INIT time is that they slow down 
  405. this process and require user interactivity in a process that 
  406. shouldn't do so.  Consider the computer that is on 24 hours a day 
  407. doing something important unattended.  The power goes off and when it 
  408. comes back on the machine reboots.  The user returns several hours 
  409. later to find that the machine is still in the middle of its startup 
  410. because your alert is waiting for a response.  Consider also the 
  411. hapless INIT writer who has to reboot his machine 20 times a day.  
  412. Your INIT will not last long on my, um, his machine.
  413.  
  414. One additional annoyance is that you need to call InitWindows to show 
  415. your window and this erases all the nice INIT icons on the screen.  I 
  416. hate that.
  417.  
  418. Here are a few possible alternatives.  
  419.  
  420. * Play a sound or use the Speech Manager to communicate the 
  421. information.  
  422.  
  423. * Show a different icon during startup to indicate an error.  An icon 
  424. with a red X through it is one way to do this.  You could also use 
  425. animated icons.
  426.  
  427. * If you must put up an alert then use a timer so that the alert goes 
  428. away by itself, even if the OK button isn't clicked.
  429.  
  430. * If you need to interact with the user to get some information, say 
  431. a password for a network connection, then do this once and save the 
  432. results in a preferences file.  Provide a Control Panel to change the 
  433. information.  Think of Control Panels as the interface for 
  434. extensions.
  435.  
  436. * Store descriptions of any errors that occur in a preferences file.  
  437. Have the Control Panel display this information.  Remember to clear 
  438. this information on each restart and to indicate to the user by sound 
  439. or icon that an error has occurred.
  440.  
  441. * If you need to communicate error information to the user after INIT 
  442. time then you should definitely use the Notification Manager.  For 
  443. example, MacSLIP uses the NM to show an alert indicating that the 
  444. carrier has been lost.
  445.  
  446. If you still want to show a dialog at INIT time then you need to set 
  447. up an A5 world first.  The ShowIcon7 code mentioned above shows how 
  448. to do this.  Also see the Tech Note 'Stand-Alone Code' (#256) for an 
  449. explanation and sample code for this and 'Giving the (Desk)Hook to 
  450. INITs' (247) discusses a bug that can appear when showing windows 
  451. from extensions.
  452. ftp://ftp.apple.com/dts/mac/tn/operating.system.os/os-02-deskhook-
  453. and-init.hqx
  454.  
  455. If you do use the Notification Manager from an extension to indicate 
  456. that the extension couldn't load you should use a self-disposing 
  457. Notification request.  There are several strategies for doing this.  
  458. I have some code samples for this that will be (have been?) posted to 
  459. alt.sources.mac soon.
  460.  
  461. --
  462. [15] How do I maintain compatibility with future systems?
  463.  
  464. This is tough, and the short answer is that you probably don't.  
  465. You'll notice that Apple comes out with new versions of its 
  466. extensions with each revision of the system.  Apple's extensions also 
  467. eventually disappear as their functionality is rolled into the 
  468. system.  In all likelihood you'll have to come out with new versions 
  469. of your extensions as new versions of the system come out as well.
  470.  
  471. Having said all that, there are things you can do to minimize this 
  472. problem.  Here are a few suggestions:
  473.  
  474. * Minimize your reliance on undocumented features of the system.
  475.  
  476. * Minimize your reliance on low memory globals.  Use the Universal 
  477. Header access 'functions' for accessing the low memory globals if 
  478. necessary.
  479.  
  480. * Use system features like Gestalt, the Process Manager, and the 
  481. Notification Manager to get information about the system, and to 
  482. communicate with the user.
  483.  
  484. * Try to patch as few traps as possible and use non-patching methods 
  485. whenever possible (e.g., use a jGNEFilter instead of patching 
  486. WaitNextEvent; the filter is more likely to remain compatible than 
  487. your patch).  
  488.  
  489. * Don't use self-modifying code.  Self-modifying code changes the 
  490. instructions from what they were compiled as, to something else, at 
  491. run-time.  The classic example is to change the address in a JMP 
  492. instruction at run-time so that it jumps to the address of the 
  493. previous trap.  This may seem faster than using a global variable but 
  494. it is only slightly faster.  It is harder to write, debug, and 
  495. maintain self-modifying code, and it is definitely more likely to 
  496. break with new system releases.  If you do use self-modifying code, 
  497. remember to flush the cache.  See the tech note 'Cache As Cache Can' 
  498. (#261) for more information on that subject.
  499.  
  500. * Use the Universal Headers for writing your extensions.  This will 
  501. help to ease the transition when it comes.
  502.  
  503. --
  504. [16] Any tips for INIT writing?
  505.  
  506. You may not want your INIT to actually do anything until after INIT 
  507. time.  You can find the end of INIT time if you have a jGNEFilter 
  508. installed when the first null event occurs.  The Notification Manager 
  509. doesn't usually start processing requests until INIT time is over so 
  510. posting a faceless notification request is another method to find the 
  511. end of INIT time (probably the best if you don't need a jGNEFilter 
  512. for something else).  You can also patch Launch to find the end of 
  513. INIT time but this is trickier.
  514.  
  515. Unfortunately some extension writers do put up windows during INIT 
  516. time.  Because of this it's possible that events will occur before 
  517. INIT time is over.  To fail-safe the above approaches you also need 
  518. to check for the presence of the Process Manager with a Gestalt call.  
  519. The Process Manager isn't available until after INIT time.  If 
  520. Gestalt reports that the Process Manager is not available then you 
  521. need to reinstall your NM request, or simply wait for another event 
  522. to be reported to your jGNEFilter when the Process Manager is 
  523. available.
  524.  
  525. Here are some utility routines that can reduce the need for 68K 
  526. assembler in your extensions:
  527.  
  528. pascal void SetA0( void* ) = { 0x205F };
  529. pascal void SetA1( void* ) = { 0x225F };
  530. void *   GetA0( void ) = { 0x2008 };
  531. void *   GetA7( void ) = { 0x200F };
  532.  
  533.  
  534. ---------------------------------------------------------------------
  535. --
  536.  
  537. Trap Patches:
  538.  
  539. --
  540. [17] What exactly is a trap patch?
  541.  
  542. A trap patch is a method for changing the functionality of a trap.  
  543. The addresses of all the traps are maintained in the two trap 
  544. dispatch tables.  By using the routine NSetTrapAddress and friends 
  545. you can change the address of a particular trap to code that you 
  546. provide.  When this is done at INIT time, all calls to the patched 
  547. trap from all applications will go to your code, which in most cases 
  548. will do something and then call through the existing trap in the 
  549. ROMs.  Be warned that the Finder patches some traps when it starts up 
  550. in a way that prevents previously-installed patches from executing.  
  551.  
  552. I recommend that you read the descriptions of the trap dispatch 
  553. mechanism in the 'Using Assembly Language' chapters in IM I and IV 
  554. and also in the 'Trap Manager' chapter in NIM Operating System 
  555. Utilities.
  556.  
  557. Traps come in several types based on their parameter passing 
  558. conventions.  Most toolbox traps use pascal calling conventions, 
  559. which means that all parameters are passed on the stack and the 
  560. return value, if any, is placed on the stack.  
  561.  
  562. Some traps use register-based calling conventions.  In these traps 
  563. the parameters are passed in registers and the return value is 
  564. returned in a register, usually D0.  For example all the Memory 
  565. Manager traps are register-based and the File Manager traps are also 
  566. register-based.  
  567.  
  568. Some traps are selector-based.  There are only so many spots in the 
  569. trap-dispatch tables.  In order to preserve space in these tables 
  570. selector-based traps have been developed.  In these traps a single 
  571. trap serves as the front end for a number of system routines.  The 
  572. parameters of these traps are passed in the usual manner, either on 
  573. the stack or in registers, and a selector is also passed, usually in 
  574. a register.  When the trap is called it checks the selector and then 
  575. dispatches to the appropriate routine.  Patching each of these types 
  576. of traps involves different mechanisms.  We'll look at samples of 
  577. each one.
  578.  
  579. Patching traps on the PowerMac is a bit different than on the 68K 
  580. Macs.  Most of the discussion here is aimed at patching traps on the 
  581. 68K Macs.  Hopefully I'll learn some more about this subject soon and 
  582. there will be some better info here on patching traps on the 
  583. PowerMac.
  584.  
  585. --
  586. [18] What's the difference between a head patch and a tail patch?
  587.  
  588. It is most common to add some functionality to a trap when patching 
  589. it rather than just replacing the existing trap.  For instance I've 
  590. written an extension that speaks the text in alerts by using the 
  591. Speech Manager.  This works by patching Alert and friends.  When 
  592. Alert is called the patch gets the text that appears in the alert and 
  593. passes it to the Speech Manager.  The patch then calls the existing 
  594. Alert trap that is in the ROMs.  A patch that works in this way is 
  595. called a head patch; it does its business and then it calls the 
  596. previous trap.
  597.  
  598. A tail patch is a bit different.  A virus-checking program might want 
  599. to patch GetResource and then examine the resource that was read in 
  600. to see if it contains a virus.  In order to do this the patch must 
  601. first call the existing trap and then do its processing, and finally 
  602. return to the application.  This is a tail patch because some 
  603. processing occurs after the existing trap is called.  In order for a 
  604. patch to be a head patch you must use a jmp instruction to jump to 
  605. the previous trap.  If you use a jsr or a C function pointer to jump 
  606. to the previous trap you have a tail patch.
  607.  
  608. The reason that this head and tail patch business has been so 
  609. important in the past is because of an Apple invention called the 
  610. come-from patch.  Patches were invented, of course, so that Apple 
  611. could fix bugs in the ROMs and could update the routines in the ROMs 
  612. with software.  This is why you can run system 7 on a Mac Plus, whose 
  613. ROMs are obviously missing most of the additions made to the toolbox 
  614. in recent years.  
  615.  
  616. Certain ROM routines are particularly large so it is inconvenient to 
  617. patch them if they have bugs in them.  To get around this problem the 
  618. Apple programmers searched for smaller routines that are called from 
  619. the large buggy routines and placed patches in the smaller routines.  
  620. These patches check the return address on the stack.  If it is the 
  621. address of the buggy routine then a fix is applied.  If not then they 
  622. just go on as usual.  The patches to these smaller routines are known 
  623. as come-from patches.  If you tail patch one of these and then call 
  624. the existing come-from patch, the return address on the stack will be 
  625. in your patch and not the buggy routine that called you.  In this 
  626. case the come-from patch will not apply its fix and your system will 
  627. crash.  
  628.  
  629. The good news is that as of system 7 it is safe to apply tail 
  630. patches.  The come-from patches still exist in system software, but 
  631. NGetTrapAddress has been modified to return an address that is safe 
  632. to use when applying tail-patches.  However, if you wish your 
  633. extension to run in System 6 then tail-patches are not allowed.  This 
  634. is documented in the Trap Manager chapter in NIM: OS Utilities.
  635.  
  636. If you absolutely positively need a tail patch in System 6 then the 
  637. following logic may apply: (Just don't tell anyone that I told you 
  638. this :-)  Apple is not releasing any new versions of System 6 so no 
  639. new come-from patches will be forthcoming for System 6.  If you do 
  640. careful testing of the traps you wish to tail-patch you will probably 
  641. be OK.  In general traps not called from the ROMs, like Alert and 
  642. MenuKey, will not contain come-from patches.
  643.  
  644. One final thing: In system 7 it is safe to apply tail-patches to all 
  645. traps except FrontWindow.
  646.  
  647. --
  648.  
  649. --------------------comp.sys.mac.programmer.info---------------------
  650. comp.sys.mac.programmer.info  is  primarily  for  distributing  FAQs,
  651. tutorials, news, and  similar  documents  related to programming  the
  652. Macintosh.  To post, email csmp_info@xplain.com
  653. -----------------------about MacTech Magazine----------------------
  654. PO Box 250055, Los Angeles, CA 90025, 310-575-4343, Fax:310-575-0925
  655. For more info, anonymous ftp to ftp.netcom.com and cd to /pub/xplain
  656. or email to the following @xplain.com : custservice, editorial, 
  657. adsales, marketing, accounting, pressreleases, progchallenge, 
  658. publisher, info
  659.